# gozerlib/users.py
#
#

""" bot users """

## gozerlib imports

from gozerlib.utils.xmpp import stripped
from gozerlib.config import ownercheck

## google imports

from google.appengine.ext import db

## basic imports

import os
import time
import logging
import re
import types
import time

class GozerUser(db.Expando):

    """ blipmaker users model. """

    modtime = db.DateTimeProperty(auto_now=True)
    createtime = db.DateTimeProperty(auto_now_add=True)                        
    name = db.StringProperty(required=True)
    email = db.StringProperty()
    wave = db.StringProperty()
    web = db.StringProperty()
    xmpp = db.StringProperty()
    userhosts = db.StringListProperty(required=True, indexed=False)
    perms = db.StringListProperty()
    permits = db.StringListProperty()
    statuses = db.StringListProperty()
    password = db.StringProperty()

class GozerUserhost(db.Expando):

    """ used to lookup userhost based on name. """

    modtime = db.DateTimeProperty(auto_now=True)
    createtime = db.DateTimeProperty(auto_now_add=True)                        
    name = db.StringProperty(required=True)
    userhost = db.StringProperty(required=True)
    source = db.StringProperty()
    
def _getuser(userhost, source=None):

    """ get a user based on userhost. """
    
    uhs = db.GqlQuery("SELECT * FROM GozerUserhost WHERE userhost = :1", userhost).fetch(100)

    if not uhs:
        logging.info('no userhost found for %s' % userhost)
        return

    xmpp = ""
    wave = ""
    web = ""
    nosource = ""

    for uh in uhs:
        user = db.GqlQuery("SELECT * FROM GozerUser WHERE name = :1", uh.name).get()

        if uh.source == 'xmpp': xmpp = user
        elif uh.source == 'wave': wave = user
        elif uh.source == 'web': web = user
        else: nosource = user

    u = xmpp or wave or nosource or web

    if u:
        return u

    logging.info('no user found for %s' % userhost)

def byname(name, source=None):

    """ get a users based on name. """
    if source:
        user = db.GqlQuery("SELECT * FROM GozerUser WHERE name = :1 AND source = :2", name, source).get()
    else:
        user = db.GqlQuery("SELECT * FROM GozerUser WHERE name = :1", name).get()

    return user

class GozerUsers(object):

    """
         google datbase engine backed users.

    """

    ### Misc. Functions
    def size(self):

        """
            return nr of users.

        """

        result = db.GqlQuery("SELECT * FROM GozerUser").fetch(1000)
        return len(result)
        

    def getuser(self, userhost, source=None):

        """ 
            get a user by userhost .. if source is not provided scan for xmpp,
            then wave and if that fails check web.
        """

        return _getuser(userhost)
 
    def names(self):

        """
            get names of all users.

            :rtype: list .. list of usernames

        """
        return [n.name for n in GozerUser.all()]

    def merge(self, name, userhost):

        """
             add userhosts to user with name.

             :param name: name of the user to merge with
             :type name: string
             :param userhost: userhosts to merge 
             :type userhost: list .. list of userhosts
             :rtype: boolean .. whether the merge succeeded

        """

        user = byname(name)

        if user:

            if userhost not in user.userhosts:
                user.userhosts.append(userhost)
                user.put()
                uh = GozerUserhost(name=name, userhost=userhost, source=ievent.type)
                uh.put()
                logging.info("%s merged with %s" % (userhost, name))
                return True

        return False

    def usersearch(self, userhost):

        """
            search for users with a userhost like the one specified.

            :param userhost: userhost to search for
            :type userhost: string
            :rtype: list .. list of (user.name, user.userhost) tuples

        """

        return [(u.name, u.userhosts) for u in GozerUser.all()]

    ### Check functions
    def exists(self, name, source=None):

        """
            see if user with <name> exists.

            :param name: name of the user
            :rtype: boolean

        """

        a = byname(name, source)

        if a:
            return True

        return False

    def allowed(self, userhost, perms, log=True, source=None):

        """
            check if user with userhosts is allowed to execute perm command.

            :param userhost: userhost to check
            :type userhost: string
            :param perms: the permissions to check            
            :type perms: list
            :param log: whether the check should be logged
            :type log: boolean
            :rtype: list .. list of matching permissions or None
 
        """

        if ownercheck(userhost):

            if not self.exists(userhost):
                self.add(userhost, [userhost, ], ['USER', 'OPER'], source)

            return ['USER', 'OPER']

        if not type(perms) == types.ListType:
            perms = [perms, ]

        if 'ANY' in perms:
            return ['ANY', ]

        res = None
        user = self.getuser(userhost)

        if not user:

            if log:
                logging.info('%s userhost denied' % userhost)
            return res

        else:
            uperms = set(user.perms)
            sperms = set(perms)
            intersection = sperms.intersection(uperms)
            res = list(intersection) or None

        if not res and log:
            logging.info("%s perm %s denied" % (userhost, str(perms)))

        if res and log:
            logging.info('allowed %s %s perm' % (userhost, str(perms)))

        return res

    def permitted(self, userhost, who, what):

        """
            check if (who,what) is in users permit list.

            :param userhost: userhost to check permits for
            :type userhost: string
            :param who: who to permit
            :type who: string
            :param what: what to allow
            :type what: string

        """

        user = self.getuser(userhost)
        res = False

        if user:
            if '%s %s' % (who, what) in user.permits:
                res = True

        return res

    def status(self, userhost, status):

        """
            check if user with <userhost> has <status> set.

        """

        user = self.getuser(userhost)
        res = False

        if user:
            if status.upper() in user.statuses:
                res = True

        return res

    def gotuserhost(self, name, userhost):

        """
            check if user has userhost.

        """

        user = byname(name)

        if user:
            return userhost in user.userhosts

        return False

    def gotperm(self, name, perm):

        """
            check if user has permission.

        """

        user = byname(name)

        if user:
            return perm.upper() in user.perms

        return False

    def gotpermit(self, name, permit):

        """
            check if user permits something.  permit is a [who, what] list. 

        """

        user = byname(name)

        if user:
            return '%s %s' % permit in user.permits

        return ""

    def gotstatus(self, name, status):

        """
            check if user has status.

        """

        user = byname(name)
        if user:
            return status.upper() in user.statuses

        return False

    ### Get Functions
    def getname(self, userhost, source=None):

        """
            get name of user belonging to <userhost>.

        """

        user = self.getuser(userhost)

        if user:
            return str(user.name)

    def gethosts(self, userhost):

        """ 
            return the userhosts of the user associated with the 
            specified userhost.

        """

        user = self.getuser(userhost)

        if user:
            return list(user.userhosts)

        return []
    
    def getemail(self, userhost):

        """
            return the email of the specified userhost.

        """

        user = self.getuser(userhost)

        if user:
            if user.email:
                return str(user.email)

        return ""

    def getperms(self, userhost):

        """
            return permissions of user.

        """

        user = self.getuser(userhost)

        if user:
            return list(user.perms)

        return []

    def getpermits(self, userhost):

        """
            return permits of the specified userhost.

        """

        user = self.getuser(userhost)

        if user:
            return list(user.permits)

        return []

    def getstatuses(self, userhost):

        """
            return the list of statuses for the specified userhost.

        """

        user = self.getuser(userhost)

        if user:
            return list(user.statuses)

    def getuserhosts(self, name):

        """
            return the userhosts associated with the specified user.

        """

        user = byname(name)

        if user:
            return list(user.userhosts)

    def getuseremail(self, name):

        """
            get email of user.

        """

        user = byname(name)

        if user:
            if user.email:
                return str(user.email)

    def getuserperms(self, name):

        """
            return permission of user.

        """

        user = byname(name)

        if user:
            return list(user.perms)

    def getuserpermits(self, name):
 
        """
            return permits of user.

        """

        user = byname(name)

        if user:
            return list(user.permits)

    def getuserstatuses(self, name):

        """
            return the list of statuses for the specified user.

        """

        user = byname(name)

        if user:
            return list(user.statuses)

    def getpermusers(self, perm):

        """
            return all users that have the specified perm.

        """
        return [user.name for user in GozerUser.all() if perm in user.perms]

    def getstatususers(self, status):

        """
            return all users that have the specified status.

        """

        return [user.name for user in GozerUser.all() if status in user.statuses]

    ### Set Functions
    def setemail(self, name, email):

        """
            set email of user.

        """

        user = byname(name)

        if user:
            user.email = email
            user.put()
            return True

        return False

    def addindex(self, name, userhost, source):

        """ add a GozerUserhost record pointing to user. """

        newuserhost = GozerUserhost(name=name, userhost=userhost, source=source)
        newuserhost.put()
        return newuserhost

    ### Add functions
    def add(self, name, userhosts, perms, source=None):

        """
            add an user. session argument is provided by @trans

            :param name: name of the user
            :type name: string
            :param userhosts: userhosts to add
            :type userhosts: list .. list of userhosts
            :param perms: the permissions to add
            :type perms: list .. list of permissions


        """

        if type(userhosts) != types.ListType:
            logging.info('i need a list of userhosts')
            return False

        user = byname(name)

        if not user:
            newuser = GozerUser(name=name)
            newuser.userhosts.extend(userhosts)
            newuser.perms.extend(perms)
            newuser.put()

            for userhost in userhosts:
                if source:
                    newuserhost = GozerUserhost(name=name, userhost=userhost, source=source)
                else:
                    newuserhost = GozerUserhost(name=name, userhost=userhost)
                newuserhost.put()

            logging.info('%s %s %s added to user database' % (name, userhosts, perms))

        return True

    def addperm(self, userhost, perm):

        """
            add the specified perm to the userhost.

        """

        user = self.getuser(userhost)

        if user:
            user.perms.append(perm.upper())
            user.put()
            logging.info('%s perm %s added' % (userhost, perm))
            return True

        return False

    def addpermit(self, userhost, permit):

        """
            add the given [who, what] permit to the given userhost.

        """

        user = self.getuser(userhost)

        if user:
            p = '%s %s' % permit
            user.permits.append(p)
            user.put()
            logging.info('%s permit %s added' % (userhost, p))
            return True

        return False

    def addstatus(self, userhost, status):

        """
            add status to given userhost.

        """

        user = self.getuser(userhost)

        if user:
            user.statuses.append(status.upper())
            user.put()
            logging.info('%s status %s added' % (name, status))
            return True

        return False

    def adduserhost(self, name, userhost):

        """
            add userhost.

        """

        user = byname(name)

        if user:
            user.userhosts.append(userhost)
            user.put()
            newuserhost = GozerUserhost(name=name, userhost=userhost)
            newuserhost.put()

            logging.info('%s (%s) added to userhosts' % (name, userhost))
            return True

        return False

    def adduseremail(self, name, email):

        """
            add email to specified user.

        """

        user = byname(name)

        if user:
            user.email.append(email)
            user.put()
            logging.info('%s email %s added' % (name, email))
            return True

    def adduserperm(self, name, perm):

        """
            add permission.

        """

        user = byname(name)

        if user:
            perm = perm.upper()
            user.perms.append(perm)
            user.put()
            logging.info('%s perm %s added' % (name, perm))
            return True

        return False

    def adduserpermit(self, name, who, permit):

        """ 
            add (who, what) permit tuple to specified user.

        """

        user = byname(name)

        if user:
            p = '%s %s' % (who, permit)
            user.permits.append(p)
            user.put()
            logging.info('%s permit %s added' % (name, p))
            return True

        return False

    def adduserstatus(self, name, status):

        """
            add status to given user.

        """

        user = byname(name)

        if user:
            user.statuses.append(status.upper())
            user.put()
            logging.info('%s status %s added' % (name, status))
            return True

        return False


    ### Delete functions
    def delemail(self, userhost, email):

        """
            delete email from userhost.

        """

        user = self.getuser(userhost)

        if user:
            user.email = ""
            user.put()
            return True

        return False

    def delperm(self, userhost, perm):

        """
            delete perm from userhost.

        """

        user = self.getuser(userhost)

        if user:
            p = perm.upper()

            if p in user.perms:
                user.perms.remove(p)
                user.put()
                return True

        return False
            
    def delpermit(self, userhost, permit):

        """
            delete permit from userhost.

        """

        user = self.getuser(userhost)

        if user:
            p = '%s %s' % permit

            if p in user.permits:
                user.permits.remove(p)
                user.put()
                return True

        return False

    def delstatus(self, userhost, status):

        """
            delete status from userhost.

        """

        user = self.getuser(userhost)

        if user:
            st = status.upper()

            if st in user.statuses:
                user.statuses.remove(st)
                user.put()
                return True

        return False

    def delete(self, name):

        """ 
            delete user with name. 

        """
        user = byname(name)

        if user:
            user.delete()
            return True

        return False

    def deluserhost(self, name, userhost):

        """
            delete the userhost entry.

        """

        user = byname(name)

        if user:
            if userhost in user.userhosts:
                user.userhosts.remove(userhost)
                user.put()
                logging.info('%s userhost %s deleted' % (name, userhost))
                return True

        return False

    def deluseremail(self, name, email):

        """
            delete email.

        """

        user = byname(name)

        if user:
            if email in user.email:
                user.email.remove(email)
                user.put()
                logging.info('%s email %s deleted' % (name, email))
                return True

        return False

    def deluserperm(self, name, perm):

        """
             delete permission.

        """

        user = byname(name)

        if user:
            p = perm.upper()

            if p in user.perms:
                user.perms.remove(p)
                user.put()
                logging.info('%s perm %s deleted' % (name, p))
                return True

        return False

    def deluserpermit(self, name, permit):

        """
            delete permit.

        """

        user = byname(name)

        if user:
            p = '%s %s' % permit

            if p in user.permits:
                user.permits.remove(p)
                user.put()
                logging.info('%s permit %s deleted' % (name, p))
                return True

        return False

    def deluserstatus(self, name, status):

        """
            delete the status from the given user.

        """

        user = byname(name)

        if user:
            st = status.upper()

            if st in user.statuses:
                user.statuses.remove(status)
                user.put()
                logging.info('%s status %s deleted' % (name, st))
                return True

        return False

    def delallemail(self, name):

        """
            Delete all emails of the specified user.

        """

        user = byname(name)

        if user:
            user.email = ""
            user.put()
            logging.info('%s emails deleted' % (name, ))
            return True

        return False

    def delpermall(self, perm):

        """
            delete permission from all users.

        """

        users = GozerUser.all().fetch()

        for user in users:

            if user.name != 'owner':

                try:
                    user.perms.remove(perm)

                except ValueError:
                    pass
        users.put()
        return True

    def make_owner(self, userhosts):

        """
            see if owner already has a user account if not merge otherwise 
            add.

            :param userhosts: userhosts to inititalize owner with
            :type userhosts: list or string

        """

        owner = []

        if type(userhosts) != types.ListType:
            owner.append(userhosts)
        else:
            owner = userhosts

        for userhost in owner:
            username = self.getname(unicode(userhost))

            if not username:
                if not self.merge('owner', unicode(userhost)):
                    self.add('owner', [unicode(userhost), ], ['USER', 'OPER'])


users = GozerUsers()
